﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.GamerServices;
using ERF;

namespace ERF
{
    /// <summary>
    /// This class handles all input for the game. It provides an abstraction for all input, in order
    /// to make the game compatible with both computers and XBOX 360
    /// </summary>
    public class ERFInputHandler : GameComponent, IERFInputHandler
    {

        private Vector2 analogDeviceLocation;
        private Vector2 oldAnalogDeviceLocation;
        private int zoomValue = 0;
        private int oldZoomValue = 0;

        private MouseState newMouseState;
        private MouseState oldMouseState;
        private PlayerIndex playerIndex = PlayerIndex.One;

        public PlayerIndex PlayerIndex
        {
            get { return playerIndex; }
            set { playerIndex = value; }
        }
        private Dictionary<String, AssignedButton> buttonConfig;
        private Dictionary<String, TimeSpan> keyTime;
        private const int KEY_REPEAT = 100;

#if XBOX
        private GamePadState newState;

        public GamePadState NewState
        {
            get { return newState; }
            set { newState = value; }
        }
        private GamePadState oldState;

        public GamePadState OldState
        {
            get { return oldState; }
            set { oldState = value; }
        }
        
#else
        private KeyboardState newState;
        private KeyboardState oldState;
        /// <summary>
        /// Gets or sets the KeyboardState retrieved in the previous call to Update.
        /// </summary>
        internal KeyboardState OldState
        {
            get { return oldState; }
            set { oldState = value; }
        }

        /// <summary>
        /// Gets or sets the KeyboardState retrieved in the most recent call to Update.
        /// </summary>
        internal KeyboardState NewState
        {
            get { return newState; }
            set { newState = value; }
        }
        
        /// <summary>
        /// Gets or sets the MouseState revrieved in the previous call to update.
        /// </summary>
        internal MouseState OldMouseState
        {
            get { return oldMouseState; }
            set { oldMouseState = value; }
        }
        
        /// <summary>
        /// Gets or sets the MouseState retrieved in the most recent call to update.
        /// </summary>
        internal MouseState NewMouseState
        {
            get { return newMouseState; }
            set { newMouseState = value; }
        }      
#endif

        /// <summary>
        /// Returns a collection of key-value pairs denoting the current button config.
        /// </summary>
        public Dictionary<String, AssignedButton> ButtonConfig
        {
            get { return buttonConfig; }
        }
        
        /// <summary>
        /// This property provides you with the location of the analog device. 
        /// This could be the thumbstick on the gamepad or the mouse.
        /// </summary>
        public Vector2 AnalogDeviceLocation
        {
            get
            {
                return analogDeviceLocation;
            }
            
        }

        /// <summary>
        /// This property provides you with the old location of the analog device. 
        /// This could be the thumbstick on the gamepad or the mouse.
        /// </summary>
        public Vector2 OldAnalogDeviceLocation
        {
            get
            {
                return oldAnalogDeviceLocation;
            }

        }

        /// <summary>
        /// Get the current zoom value from the Gamepad or the mouse scroll wheel
        /// </summary>
        public int ZoomValue
        {
            get { return zoomValue; }
            set { zoomValue = value; }
        }

        /// <summary>
        /// Get the old zoom value from the Gamepad or the mouse scroll wheel
        /// </summary>
        public int OldZoomValue
        {
            get { return oldZoomValue; }
            set { oldZoomValue = value; }
        }

        /// <summary>
        /// Creates a new ERFInputHandler.
        /// </summary>
        public ERFInputHandler(Game game) : base(game)
        {
            buttonConfig = new Dictionary<String, AssignedButton>();
            keyTime = new Dictionary<String, TimeSpan>();

            this.Game.Services.AddService(typeof(IERFInputHandler), this);
/*         
#if XBOX
            AssignXboxKeys();
#else
            AssignWindowsKeys();
#endif
*/
        }


        /// <summary>
        /// Updates the state of the keyboard or gamepad. 
        /// This must be done before getting button states in order to record recent key strokes.
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            oldState = newState;
            
#if XBOX
            analogDeviceLocation = newState.ThumbSticks.Right;
            newState = GamePad.GetState(playerIndex);
            
            if (newState.Buttons.LeftShoulder == ButtonState.Pressed)
            {
                zoomValue += 120;
            }
            
            if (newState.Buttons.RightShoulder == ButtonState.Pressed)
            {
                zoomValue -= 120;
            }
#else
            Vector2 mouseResetPosition = new Vector2(base.Game.GraphicsDevice.Viewport.Width, base.Game.GraphicsDevice.Viewport.Height) / 2f;
            this.newState = Keyboard.GetState();
            this.oldMouseState = newMouseState;
            this.newMouseState = Mouse.GetState();
            this.oldAnalogDeviceLocation = mouseResetPosition; //analogDeviceLocation;
            if (Game.IsActive)
            {
                this.analogDeviceLocation = new Vector2(newMouseState.X, newMouseState.Y);
                Mouse.SetPosition((int)mouseResetPosition.X, (int)mouseResetPosition.Y);
            }
            this.oldZoomValue = zoomValue;
            this.zoomValue = newMouseState.ScrollWheelValue;


#endif

            base.Update(gameTime);
        }

        /// <summary>
        /// Get the state of the button. The state was recorded during the last call to the update method.
        /// </summary>
        /// <param name="command"></param>
        /// <param name="gameTime"></param>
        /// <param name="acceptRepeat"></param>
        /// <returns></returns>
        public KeyState GetButtonState(String command, GameTime gameTime, bool acceptRepeat)
        {
                
                KeyState state;
                if (buttonConfig[command].IsButtonDown())
                {
                    if (acceptRepeat)
                        state = KeyState.Down;
                    else
                    {
                        if (keyTime.ContainsKey(command))
                        {
                            if ((gameTime.TotalGameTime - keyTime[command]).TotalMilliseconds >= KEY_REPEAT)
                            {
                                this.UpdateKeyTime(command, gameTime);
                                state = KeyState.Down;
                            }
                            else
                            {
                                state = KeyState.Timeout;
                            }
                        }
                        else
                        {
                            UpdateKeyTime(command, gameTime);
                            state = KeyState.Down;
                        }
                    }
                }
                
                else if (buttonConfig[command].IsButtonReleased())
                    state = KeyState.Released;
                else
                    state = KeyState.Up;

            return state;
        }

        /// <summary>
        /// Update the time for the keys in the key array
        /// </summary>
        /// <param name="key"></param>
        /// <param name="gameTime"></param>
        private void UpdateKeyTime(String command, GameTime gameTime)
        {
            if (this.keyTime.ContainsKey(command))
                this.keyTime[command] = gameTime.TotalGameTime;
            else
                this.keyTime.Add(command, gameTime.TotalGameTime);
        }

        /// <summary>
        /// Make a request to access the keyboard. This method must be
        /// called regardless of device to ensure that the user is able to
        /// do keyboard input.
        /// </summary>
        public void AccessKeyboard(string title, string description,string value,object identifier)
        {
            Guide.BeginShowKeyboardInput(playerIndex,title,description, value, new AsyncCallback(HandleKeyboardCallback), identifier);
        }

        /// <summary>
        /// This method handles the async callback from the screen keyboard.
        /// </summary>
        /// <param name="result"></param>
        private void HandleKeyboardCallback(IAsyncResult result)
        {
            string value = Guide.EndShowKeyboardInput(result);
            
        }

    }
}
